// This holds all the generic TB nodes
(namespace ___
    ////////////////////////////////
    // CONSTANTS
    ////////////////////////////////
    (node ICONST   extra=TB_NodeInt)
    (node F32CONST extra=TB_NodeFloat32)
    (node F64CONST extra=TB_NodeFloat64)

    ////////////////////////////////
    // PROJECTIONS
    ////////////////////////////////
    // projections just extract a single field of a tuple:
    //   Tuple & Int -> Any
    (node PROJ          PINNED extra=TB_NodeProj)
    (node BRANCH_PROJ   parent=PROJ extra=TB_NodeBranchProj)
    (node MACH_PROJ     parent=PROJ)

    ////////////////////////////////
    // MISCELLANEOUS
    ////////////////////////////////
    // this is an unspecified value, usually generated by the optimizer
    // when malformed input is folded into an operation.
    (node POISON)        // () -> Any
    (node CYCLE_COUNTER) // (Control) -> Int64
    // prefetches data for reading. The number next to the
    //
    //   0   is temporal
    //   1-3 are just cache levels
    (node PREFETCH extra=TB_NodePrefetch) // (Memory, Ptr) & Int -> Memory
    // this is a bookkeeping node for constructing IR while optimizing, so we
    // don't keep track of nodes while running peeps.
    (node SYMBOL_TABLE extra=TB_NodeSymbolTable)

    ////////////////////////////////
    // CONTROL
    ////////////////////////////////
    //   there's only one ROOT per function, it's inputs are the return values, it's
    //   outputs are the initial params.
    //
    //   (Callgraph, Exits...) -> (Control, Memory, RPC, Data...)
    (node ROOT          PINNED TERMINATOR END)
    //   return nodes feed into ROOT, jumps through the RPC out of this stack frame.
    //
    //   (Control, Memory, RPC, Data...) -> ()
    (node RETURN        PINNED TERMINATOR END)

    // Regions are used to represent paths which have multiple entries.
    // each input is a predecessor.
    (node REGION        PINNED CTRL extra=TB_NodeRegion)
    (node NATURAL_LOOP  parent=REGION)       // natural loop where the first edge dominates, and the other paths are backedges.
    (node AFFINE_LOOP   parent=NATURAL_LOOP) // loop header with affine bounds.
    // Phi nodes work the same as in SSA CFG, the value is based on which predecessor was taken.
    // each input lines up with the regions such that region.in[i] will use phi.in[i+1] as the
    // subsequent data.
    (node PHI           PINNED)

    // CProj0 is the default key, every CProjN is all key-match
    (node BRANCH extra=TB_NodeBranch)
    // if the condition is non-zero, it'll take the CProj0 else CProj1
    (node IF            PINNED CTRL TERMINATOR FORK_CTRL extra=TB_NodeIf)
    (node AFFINE_LATCH  parent=IF)
    // NeverBranch is a fake branch which acts as a backedge for infinite
    // loops, this keeps the graph from getting disconnected with the endpoint.
    (node NEVER_BRANCH  PINNED CTRL TERMINATOR FORK_CTRL)
    //   debugbreak will trap in a continuable manner.
    (node DEBUGBREAK    PINNED CTRL MEMORY_IN EFFECT)
    (node TRAP          PINNED CTRL TERMINATOR END MEMORY_IN EFFECT) // trap will not be continuable but will stop execution.
    (node UNREACHABLE   PINNED CTRL TERMINATOR END MEMORY_IN EFFECT) // unreachable means it won't trap or be continuable.
    (node DEAD)                                               // all dead paths are stitched here
    (node DEAD_STORE    PINNED MEMORY_IN MEMORY_OUT extra=TB_NodeMemAccess)
    (node ENTRY_FORK    PINNED CTRL TERMINATOR FORK_CTRL)

    ////////////////////////////////
    // CONTROL + MEMORY
    ////////////////////////////////
    // Nothing special, it's just a function call, 3rd argument here is the
    // target pointer (or syscall number or RPC depending on the call type)
    // and the rest are just data args.
    //
    //   (Control, Memory, Ptr, Data...) -> (Control, Memory, Data)
    (node CALL          PINNED CTRL MEMORY_IN MEMORY_OUT SAFEPOINT EFFECT extra=TB_NodeCall)
    (node SYSCALL       parent=CALL)
    (node TAILCALL      END parent=CALL)

    // This is a safepoint used for traditional C debugging, each of these nodes
    // annotates a debug line location.
    (node DEBUG_LOCATION PINNED CTRL MEMORY_IN MEMORY_OUT extra=TB_NodeDbgLoc) // (Control, Memory) -> (Control, Memory)
    // This special op tracks calls such that we can produce our cool call graph, there's
    // one call graph node per function that never moves.
    (node CALLGRAPH)      // (Call...) -> Void
    (node DEBUG_SCOPES)   // (Parent, Control...)

    ////////////////////////////////
    // MEMORY
    ////////////////////////////////
    //   produces a set of non-aliasing memory effects
    (node SPLITMEM)    // (Memory) -> (Memory...)
    //   MERGEMEM will join multiple non-aliasing memory effects, because
    //   they don't alias there's no ordering guarentee.
    (node MERGEMEM)    // (Split, Memory...) -> Memory
    //   LOAD and STORE are standard memory accesses, they can be folded away.
    (node LOAD          MEMORY_IN extra=TB_NodeMemAccess)                   // (Control?, Memory, Ptr)      -> Data
    (node STORE         MEMORY_IN MEMORY_OUT extra=TB_NodeMemAccess)        // (Control, Memory, Ptr, Data) -> Memory
    //   bulk memory ops.
    (node MEMCPY        MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeMemAccess) // (Control, Memory, Ptr, Ptr, Size)  -> Memory
    (node MEMSET        MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeMemAccess) // (Control, Memory, Ptr, Int8, Size) -> Memory
    // Atomics have multiple observers (if not they wouldn't need to
    // be atomic) and thus produce side effects everywhere just like
    // volatiles except they have synchronization guarentees. the atomic
    // data ops will return the value before the operation is performed.
    (node ATOMIC_LOAD   MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeAtomic) // (Control, Memory, Ptr)        -> (Memory, Data)
    (node ATOMIC_XCHG   MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeAtomic) // (Control, Memory, Ptr, Data)  -> (Memory, Data)
    (node ATOMIC_ADD    MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeAtomic) // (Control, Memory, Ptr, Data)  -> (Memory, Data)
    (node ATOMIC_AND    MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeAtomic) // (Control, Memory, Ptr, Data)  -> (Memory, Data)
    (node ATOMIC_XOR    MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeAtomic) // (Control, Memory, Ptr, Data)  -> (Memory, Data)
    (node ATOMIC_OR     MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeAtomic) // (Control, Memory, Ptr, Data)  -> (Memory, Data)
    (node ATOMIC_PTROFF MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeAtomic) // (Control, Memory, Ptr, Ptr)   -> (Memory, Ptr)
    // Atomic CAS returns the old value and a boolean for success (true if
    // the value was changed)
    (node ATOMIC_CAS    MEMORY_IN MEMORY_OUT EFFECT extra=TB_NodeAtomic) // (Control, Memory, Data, Data) -> (Memory, Data, Bool)
    //   volatile memory barrier
    (node HARD_BARRIER  MEMORY_IN MEMORY_OUT EFFECT) // (Control, Memory, MemOp) -> Memory

    ////////////////////////////////
    // POINTERS
    ////////////////////////////////
    //   LOCAL will statically allocate stack space
    (node LOCAL NO_GVN extra=TB_NodeLocal) // () & (Int, Int) -> Ptr
    //   SYMBOL will return a pointer to a (node Symbol
    (node SYMBOL extra=TB_NodeSymbol)      // () & (node Symbol* -> Ptr
    //   offsets pointer by byte amount (handles all ptr math you actually want)
    (node PTR_OFFSET)    // (Ptr, Int) -> Ptr

    // Conversions
    (node TRUNCATE)
    (node FLOAT_TRUNC)
    (node FLOAT_EXT)
    (node SIGN_EXT)
    (node ZERO_EXT)
    (node UINT2FLOAT)
    (node FLOAT2UINT)
    (node INT2FLOAT)
    (node FLOAT2INT)
    (node BITCAST)

    // Select
    (node SELECT)

    // Bitmagic
    (node BSWAP)
    (node CLZ)
    (node CTZ)
    (node POPCNT)

    // Unary operations
    (node FNEG)

    // Integer arithmatic
    (node AND extra=TB_NodeBinopInt)
    (node OR  extra=TB_NodeBinopInt)
    (node XOR extra=TB_NodeBinopInt)
    (node ADD extra=TB_NodeBinopInt)
    (node SUB extra=TB_NodeBinopInt)
    (node MUL extra=TB_NodeBinopInt)

    (node SHL extra=TB_NodeBinopInt)
    (node SHR extra=TB_NodeBinopInt)
    (node SAR extra=TB_NodeBinopInt)
    (node ROL extra=TB_NodeBinopInt)
    (node ROR extra=TB_NodeBinopInt)
    (node UDIV extra=TB_NodeBinopInt)
    (node SDIV extra=TB_NodeBinopInt)
    (node UMOD extra=TB_NodeBinopInt)
    (node SMOD extra=TB_NodeBinopInt)

    // Float arithmatic
    (node FADD)
    (node FSUB)
    (node FMUL)
    (node FDIV)
    (node FMIN)
    (node FMAX)

    // Comparisons
    (node CMP_EQ    COMPARE extra=TB_NodeCompare)
    (node CMP_NE    COMPARE extra=TB_NodeCompare)
    (node CMP_ULT   COMPARE extra=TB_NodeCompare)
    (node CMP_ULE   COMPARE extra=TB_NodeCompare)
    (node CMP_SLT   COMPARE extra=TB_NodeCompare)
    (node CMP_SLE   COMPARE extra=TB_NodeCompare)
    (node CMP_FLT   COMPARE extra=TB_NodeCompare)
    (node CMP_FLE   COMPARE extra=TB_NodeCompare)

    // Extra float ops
    (node FSQRT)

    (node FRAME_PTR)
    // Consumes a value to force it to stay alive
    (node BLACKHOLE CTRL EFFECT) // (Control) -> (Control)

    // Special ops
    //   does full multiplication (64x64=128 and so on) returning
    //   the low and high values in separate projections
    (node SMULPAIR)
    (node UMULPAIR)

    // Vector ops
    (node VBROADCAST)
    (node VSHUFFLE)

    // variadic
    (node VA_START)

    // general machine nodes:
    (node MACH_COPY extra=TB_NodeMachCopy)
    // (Control) -> Control
    (node MACH_JUMP)
    // just... it) idk) it's the frame ptr
    (node MACH_FRAME_PTR PINNED)
    // thread-local JIT context
    (node MACH_JIT_THREAD_PTR)
    // isn't the pointer value itself) just a placeholder for
    // referring to a global.
    (node MACH_SYMBOL extra=TB_NodeMachSymbol)
    (node MACH_TEMP NO_GVN extra=TB_NodeMachTemp)
)

#ifdef TB_HAS_X64
#include "x64/x64.dsl"
#endif

